#include "file_interface.h"
#include "file_manager.h"
#include <cstdio>
#include <exception>
#include <boost/thread/locks.hpp>

namespace egear{namespace file{ // egear::file

/********************** FileInterface *******************/

FileInterface::FileInterface(uint32_t _index,const std::string &_folder,
		    const std::string &_path, int64_t _size){
    index = _index;
    path = _path;
    pathOnDisk = _folder + "/" + path;
    size = _size;
    file = NULL;
};

FileInterface::~FileInterface(){
    close();
};

void FileInterface::open() throw(IOException) {
    if(file!=NULL) {
	throw IOException("file is already open");
    }
    if((file = fopen(pathOnDisk.c_str(), "rb+"))==NULL) {
	file = fopen(pathOnDisk.c_str(), "wb+");
    }
};
void FileInterface::close() {
    if(file!=NULL) {
	fclose(file);
	file = NULL;
    };
};

bool FileInterface::isOpen(){
    return (file != NULL);
};

int64_t FileInterface::write(int64_t offset, const std::string &data)
	throw(IOException) {
    fseek(file, offset, SEEK_SET);
    if(fwrite(data.c_str(), 1, data.size(), file) != data.size()){
	throw IOException("file write error!");
    }
    return data.size();
};

std::string FileInterface::read(int64_t offset, int len)
	throw(IOException) {
    fseek(file, offset, SEEK_SET);
    char *buf = new char[len];
    int rlen;
    if((rlen=fread(buf, 1, len, file))==EOF){
	delete buf;
	throw IOException("file read error!");
    }
    std::string data;
    data.insert(0, buf, rlen);
    delete buf;
    return data;
};

/************************** SuspenseFile *****************************/
SuspenseFile::SuspenseFile(uint32_t _index,const std::string &_folder,
		    const std::string &_path, int64_t _size)
	:FileInterface(_index, _folder, _path, _size)
{
    // the size is already known.
    digestFile = NULL;
    if(size!=-1) {
	/*
	 * How many pieces we want to divided the file to.
	 */
	pieceAmount = size / PieceSize; 
        if(size % PieceSize) pieceAmount++; // The last piece is
	                                    // not a full piece
        workingMask.resize(size);
	doneMask.resize(size);
    }
};

SuspenseFile::~SuspenseFile(){
    if(digestFile != NULL){
        fclose(digestFile);
    }
};

int SuspenseFile::resize(int64_t _size) {
    if(size!=-1) {
        if(size==_size) {
	    return 0;
	}
	else { // wanna set it to another size ?
	    return -1;
	}
    }
    size = _size;
    pieceAmount = size / PieceSize; 
    if(size % PieceSize) pieceAmount++; // The last piece is
	                                    // not a full piece
    workingMask.resize(size);
    doneMask.resize(size);
};

int SuspenseFile::lockPiece(int pieceIndex) {
    if(workingMask[pieceIndex] || doneMask[pieceIndex]) {
	return -1;
    } else {
	workingMask[pieceIndex] = true;
	return 0;
    }
};

void SuspenseFile::releasePiece(int pieceIndex) {
    workingMask[pieceIndex]=false;
};

void SuspenseFile::setPieceDone(int pieceIndex) {
    doneMask[pieceIndex]=true;
};

/*********************** SyncSuspenseFile *************************/
SyncSuspenseFile::SyncSuspenseFile(uint32_t _index,const std::string &_folder,
		    const std::string &_path, CurlFileManager *_fileManager,
		    int64_t _size)
	:SuspenseFile(_index, _folder, _path, _size) {
    fileManager = _fileManager;

};

SyncSuspenseFile::~SyncSuspenseFile(){

};

int64_t SyncSuspenseFile::write(int64_t offset, const std::string &data)
	throw(IOException) {
    boost::lock_guard<boost::mutex> locker(mutex);
    return SuspenseFile::write(offset, data);
}

std::string SyncSuspenseFile::read(int64_t offset, int len) 
	throw(IOException) {
    boost::lock_guard<boost::mutex> locker(mutex);
    return SuspenseFile::read(offset, len);
};



}} // egear::file

